

<!DOCTYPE html>
<html lang="en" data-default-color-scheme=auto>



<head>
  <meta charset="UTF-8">
  <link rel="apple-touch-icon" sizes="76x76" href="/img/favicon.png">
  <link rel="icon" href="/img/avatar.png">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, shrink-to-fit=no">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  
  <meta name="theme-color" content="#2f4154">
  <meta name="author" content="Hughie">
  <meta name="keywords" content="">
  
    <meta name="description" content="并发编程笔记（一）基础本博客根据黑马程序员全面深入学习Java并发编程，JUC并发编程全套教程学习而做的笔记 一、基本概念1、进程与线程进程 程序由指令和数据组成，但这些指令要运行，数据要读写，就必须将指令加载至 CPU，数据加载至内存。在指令运行过程中还需要用到磁盘、网络等设备。进程就是用来加载指令、管理内存、管理 IO 的。 当一个程序被运行，从磁盘加载这个程序的代码至内存，这时就开启了一个进">
<meta property="og:type" content="article">
<meta property="og:title" content="并发编程笔记（一）基础">
<meta property="og:url" content="https://hughie-hy.gitee.io/2022/03/05/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E7%AC%94%E8%AE%B0%EF%BC%88%E4%B8%80%EF%BC%89%E5%9F%BA%E7%A1%80/index.html">
<meta property="og:site_name" content="Hughie&#39;s blog">
<meta property="og:description" content="并发编程笔记（一）基础本博客根据黑马程序员全面深入学习Java并发编程，JUC并发编程全套教程学习而做的笔记 一、基本概念1、进程与线程进程 程序由指令和数据组成，但这些指令要运行，数据要读写，就必须将指令加载至 CPU，数据加载至内存。在指令运行过程中还需要用到磁盘、网络等设备。进程就是用来加载指令、管理内存、管理 IO 的。 当一个程序被运行，从磁盘加载这个程序的代码至内存，这时就开启了一个进">
<meta property="og:locale" content="en_US">
<meta property="og:image" content="https://hughie-hy.gitee.io/img/java.png">
<meta property="article:published_time" content="2022-03-04T16:00:00.000Z">
<meta property="article:modified_time" content="2022-03-06T12:46:31.572Z">
<meta property="article:author" content="Hughie">
<meta property="article:tag" content="JUC">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="https://hughie-hy.gitee.io/img/java.png">
  
  
  <title>并发编程笔记（一）基础 - Hughie&#39;s blog</title>

  <link  rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4/dist/css/bootstrap.min.css" />


  <link  rel="stylesheet" href="https://cdn.jsdelivr.net/npm/github-markdown-css@4/github-markdown.min.css" />
  <link  rel="stylesheet" href="https://cdn.jsdelivr.net/npm/hint.css@2/hint.min.css" />

  
    
    
      
      <link  rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@10/styles/github.min.css" />
    
  

  
    <link  rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3/dist/jquery.fancybox.min.css" />
  


<!-- 主题依赖的图标库，不要自行修改 -->

<link rel="stylesheet" href="//at.alicdn.com/t/font_1749284_ba1fz6golrf.css">



<link rel="stylesheet" href="//at.alicdn.com/t/font_1736178_lbnruvf0jn.css">


<link  rel="stylesheet" href="/css/main.css" />

<!-- 自定义样式保持在最底部 -->


  <script id="fluid-configs">
    var Fluid = window.Fluid || {};
    var CONFIG = {"hostname":"hughie-hy.gitee.io","root":"/","version":"1.8.14","typing":{"enable":true,"typeSpeed":120,"cursorChar":"_","loop":false},"anchorjs":{"enable":true,"element":"h1,h2,h3,h4,h5,h6","placement":"right","visible":"hover","icon":""},"progressbar":{"enable":true,"height_px":3,"color":"#29d","options":{"showSpinner":false,"trickleSpeed":100}},"copy_btn":true,"image_zoom":{"enable":true,"img_url_replace":["",""]},"toc":{"enable":true,"headingSelector":"h1,h2,h3,h4,h5,h6","collapseDepth":0},"lazyload":{"enable":true,"loading_img":"/img/loading.gif","onlypost":false,"offset_factor":2},"web_analytics":{"enable":false,"baidu":null,"google":null,"gtag":null,"tencent":{"sid":null,"cid":null},"woyaola":null,"cnzz":null,"leancloud":{"app_id":null,"app_key":null,"server_url":null,"path":"window.location.pathname","ignore_local":false}},"search_path":"/local-search.xml"};
  </script>
  <script  src="/js/utils.js" ></script>
  <script  src="/js/color-schema.js" ></script>
<meta name="generator" content="Hexo 5.4.1"></head>


<body>
  <header style="height: 70vh;">
    <nav id="navbar" class="navbar fixed-top  navbar-expand-lg navbar-dark scrolling-navbar">
  <div class="container">
    <a class="navbar-brand" href="/">
      <strong>Hughie</strong>
    </a>

    <button id="navbar-toggler-btn" class="navbar-toggler" type="button" data-toggle="collapse"
            data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <div class="animated-icon"><span></span><span></span><span></span></div>
    </button>

    <!-- Collapsible content -->
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav ml-auto text-center">
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/">
                <i class="iconfont icon-home-fill"></i>
                Home
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/archives/">
                <i class="iconfont icon-archive-fill"></i>
                Archives
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/categories/">
                <i class="iconfont icon-category-fill"></i>
                Categories
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/tags/">
                <i class="iconfont icon-tags-fill"></i>
                Tags
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/about/">
                <i class="iconfont icon-user-fill"></i>
                About
              </a>
            </li>
          
        
        
          <li class="nav-item" id="search-btn">
            <a class="nav-link" target="_self" href="javascript:;" data-toggle="modal" data-target="#modalSearch" aria-label="Search">
              &nbsp;<i class="iconfont icon-search"></i>&nbsp;
            </a>
          </li>
        
        
          <li class="nav-item" id="color-toggle-btn">
            <a class="nav-link" target="_self" href="javascript:;" aria-label="Color Toggle">&nbsp;<i
                class="iconfont icon-dark" id="color-toggle-icon"></i>&nbsp;</a>
          </li>
        
      </ul>
    </div>
  </div>
</nav>

    <div class="banner" id="banner" parallax=true
         style="background: url('/img/default.png') no-repeat center center;
           background-size: cover;">
      <div class="full-bg-img">
        <div class="mask flex-center" style="background-color: rgba(0, 0, 0, 0.3)">
          <div class="page-header text-center fade-in-up">
            <span class="h2" id="subtitle" title="并发编程笔记（一）基础">
              
            </span>

            
              <div class="mt-3">
  
  
    <span class="post-meta">
      <i class="iconfont icon-date-fill" aria-hidden="true"></i>
      <time datetime="2022-03-05 00:00" pubdate>
        March 5, 2022 am
      </time>
    </span>
  
</div>

<div class="mt-1">
  
    <span class="post-meta mr-2">
      <i class="iconfont icon-chart"></i>
      12k 字
    </span>
  

  
    <span class="post-meta mr-2">
      <i class="iconfont icon-clock-fill"></i>
      
      
      102 分钟
    </span>
  

  
  
    
      <!-- 不蒜子统计文章PV -->
      <span id="busuanzi_container_page_pv" style="display: none">
        <i class="iconfont icon-eye" aria-hidden="true"></i>
        <span id="busuanzi_value_page_pv"></span> 次
      </span>
    
  
</div>

            
          </div>

          
        </div>
      </div>
    </div>
  </header>

  <main>
    
      

<div class="container-fluid nopadding-x">
  <div class="row nomargin-x">
    <div class="d-none d-lg-block col-lg-2"></div>
    <div class="col-lg-8 nopadding-x-md">
      <div class="container nopadding-x-md" id="board-ctn">
        <div class="py-5" id="board">
          <article class="post-content mx-auto">
            <!-- SEO header -->
            <h1 style="display: none">并发编程笔记（一）基础</h1>
            
            <div class="markdown-body">
              <h1 id="并发编程笔记（一）基础"><a href="#并发编程笔记（一）基础" class="headerlink" title="并发编程笔记（一）基础"></a>并发编程笔记（一）基础</h1><p>本博客根据<a target="_blank" rel="noopener" href="https://www.bilibili.com/video/av81461839?from=search&seid=8445102345230304010"><strong>黑马程序员全面深入学习Java并发编程，JUC并发编程全套教程</strong></a>学习而做的笔记</p>
<h1 id="一、基本概念"><a href="#一、基本概念" class="headerlink" title="一、基本概念"></a>一、基本概念</h1><h2 id="1、进程与线程"><a href="#1、进程与线程" class="headerlink" title="1、进程与线程"></a>1、进程与线程</h2><h3 id="进程"><a href="#进程" class="headerlink" title="进程"></a>进程</h3><ul>
<li>程序由指令和数据组成，但这些指令要运行，数据要读写，就必须将指令加载至 CPU，数据加载至内存。在指令运行过程中还需要用到磁盘、网络等设备。进程就是用来加载指令、管理内存、管理 IO 的。</li>
<li>当一个程序被运行，从磁盘加载这个程序的代码至内存，这时就开启了一个进程。</li>
<li>进程就可以视为程序的一个实例。大部分程序可以同时运行多个实例进程（例如记事本、画图、浏览器 等），也有的程序只能启动一个实例进程（例如网易云音乐、360 安全卫士等）</li>
</ul>
<h3 id="线程"><a href="#线程" class="headerlink" title="线程"></a>线程</h3><ul>
<li>一个进程之内可以分为一到多个线程。</li>
<li>一个线程就是一个指令流，将指令流中的一条条指令以一定的顺序交给 CPU 执行 。</li>
<li>Java 中，线程作为小调度单位，进程作为资源分配的小单位。 在 windows 中进程是不活动的，只是作 为线程的容器</li>
</ul>
<h3 id="二者对比"><a href="#二者对比" class="headerlink" title="二者对比"></a>二者对比</h3><ul>
<li>进程基本上相互独立的，而线程存在于进程内，是进程的一个子集 进程拥有共享的资源，如内存空间等，供其内部的线程共享<ul>
<li>进程间通信较为复杂 同一台计算机的进程通信称为 IPC（Inter-process communication）</li>
<li>不同计算机之间的进程通信，需要通过网络，并遵守共同的协议，例如 HTTP</li>
</ul>
</li>
<li>线程通信相对简单，因为它们共享进程内的内存，一个例子是多个线程可以访问同一个共享变量 线程更轻量，线程上下文切换成本一般上要比进程上下文切换低</li>
</ul>
<h4 id="进程和线程的切换"><a href="#进程和线程的切换" class="headerlink" title="进程和线程的切换"></a>进程和线程的切换</h4><p><strong>上下文切换</strong></p>
<p>内核为每一个进程维持一个上下文。<strong>上下文就是内核重新启动一个被抢占的进程所需的状态。</strong>包括以下内容：</p>
<ul>
<li>通用目的寄存器</li>
<li>浮点寄存器</li>
<li>程序计数器</li>
<li>用户栈</li>
<li>状态寄存器</li>
<li>内核栈</li>
<li>各种内核数据结构：比如描绘地址空间的<strong>页表</strong>，包含有关当前进程信息的<strong>进程表</strong>，以及包含进程已打开文件的信息的<strong>文件表</strong></li>
</ul>
<p><strong>进程切换和线程切换的主要区别</strong></p>
<p>最主要的一个区别在于<strong>进程切换涉及虚拟地址空间的切换而线程不会</strong>。因为每个进程都有自己的虚拟地址空间，而<strong>线程是共享所在进程的虚拟地址空间的</strong>，因此同一个进程中的线程进行线程切换时不涉及虚拟地址空间的转换</p>
<p>页表查找是一个很慢的过程，因此通常使用cache来缓存常用的地址映射，这样可以加速页表查找，这个cache就是快表TLB（translation Lookaside Buffer，用来加速页表查找）。由于每个进程都有自己的虚拟地址空间，那么显然每个进程都有自己的页表，那么<strong>当进程切换后页表也要进行切换，页表切换后TLB就失效了</strong>，cache失效导致命中率降低，那么虚拟地址转换为物理地址就会变慢，表现出来的就是程序运行会变慢，而线程切换则不会导致TLB失效，因为线程线程无需切换地址空间，因此我们通常说线程切换要比较进程切换快</p>
<p>而且还可能出现<strong>缺页中断</strong>，这就需要操作系统将需要的内容调入内存中，若内存已满则还需要将不用的内容调出内存，这也需要花费时间</p>
<p><strong>为什么TLB能加快访问速度</strong></p>
<p>快表可以避免每次都对页号进行地址的有效性判断。快表中保存了对应的物理块号，可以直接计算出物理地址，无需再进行有效性检查</p>
<h2 id="2、并发与并行"><a href="#2、并发与并行" class="headerlink" title="2、并发与并行"></a>2、并发与并行</h2><p>并发是一个CPU在不同的时间去不同线程中执行指令。</p>
<p>并行是多个CPU同时处理不同的线程。</p>
<p>引用 Rob Pike 的一段描述：</p>
<ul>
<li>并发（concurrent）是同一时间<strong>应对</strong>（dealing with）多件事情的能力</li>
<li>并行（parallel）是同一时间<strong>动手做</strong>（doing）多件事情的能力</li>
</ul>
<h3 id="3、应用"><a href="#3、应用" class="headerlink" title="3、应用"></a>3、应用</h3><h4 id="应用之异步调用（案例1）"><a href="#应用之异步调用（案例1）" class="headerlink" title="应用之异步调用（案例1）"></a>应用之异步调用（案例1）</h4><p>以调用方角度来讲，如果</p>
<ul>
<li>需要等待结果返回，才能继续运行就是同步</li>
<li>不需要等待结果返回，就能继续运行就是异步</li>
</ul>
<p>设计<br>多线程可以让方法执行变为异步的（即不要巴巴干等着）比如说读取磁盘文件时，假设读取操作花费了 5 秒钟，如 果没有线程调度机制，这 5 秒 cpu 什么都做不了，其它代码都得暂停…</p>
<ul>
<li>比如在项目中，视频文件需要转换格式等操作比较费时，这时开一个新线程处理视频转换，避免阻塞主线程</li>
<li>tomcat 的异步 servlet 也是类似的目的，让用户线程处理耗时较长的操作，避免阻塞</li>
<li>tomcat 的工作线程 ui 程序中，开线程进行其他操作，避免阻塞 ui 线程</li>
</ul>
<p>结论</p>
<ol>
<li>单核 cpu 下，多线程不能实际提高程序运行效率，只是为了能够在不同的任务之间切换，不同线程轮流使用 cpu ，不至于一个线程总占用 cpu，别的线程没法干活</li>
<li>多核 cpu 可以并行跑多个线程，但能否提高程序运行效率还是要分情况的<ul>
<li>有些任务，经过精心设计，将任务拆分，并行执行，当然可以提高程序的运行效率。但不是所有计算任 务都能拆分（参考后文的【阿姆达尔定律】）</li>
<li>也不是所有任务都需要拆分，任务的目的如果不同，谈拆分和效率没啥意义</li>
</ul>
</li>
<li>IO 操作不占用 cpu，只是我们一般拷贝文件使用的是【阻塞 IO】，这时相当于线程虽然不用 cpu，但需要一 直等待 IO 结束，没能充分利用线程。所以才有后面的【非阻塞 IO】和【异步 IO】优化</li>
</ol>
<h1 id="二、线程的创建"><a href="#二、线程的创建" class="headerlink" title="二、线程的创建"></a>二、线程的创建</h1><h2 id="1、创建一个线程（非主线程）"><a href="#1、创建一个线程（非主线程）" class="headerlink" title="1、创建一个线程（非主线程）"></a>1、创建一个线程（非主线程）</h2><h3 id="方法一：通过继承Thread创建线程"><a href="#方法一：通过继承Thread创建线程" class="headerlink" title="方法一：通过继承Thread创建线程"></a>方法一：通过继承Thread创建线程</h3><figure class="highlight scala"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs scala">public <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CreateThread</span> </span>&#123;<br>	public static void main(<span class="hljs-type">String</span>[] args) &#123;<br>		<span class="hljs-type">Thread</span> myThread = <span class="hljs-keyword">new</span> <span class="hljs-type">MyThread</span>();<br>        <span class="hljs-comment">// 启动线程</span><br>		myThread.start();<br>	&#125;<br>&#125;<br><br><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyThread</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Thread</span> </span>&#123;<br>	<span class="hljs-meta">@Override</span><br>	public void run() &#123;<br>		<span class="hljs-type">System</span>.out.println(<span class="hljs-string">&quot;my thread running...&quot;</span>);<br>	&#125;<br>&#125;<br></code></pre></div></td></tr></table></figure>

<p>使用继承方式的好处是，在run（）方法内获取当前线程直接使用this就可以了，无须使用Thread.currentThread（）方法；不好的地方是Java不支持多继承，如果继承了Thread类，那么就不能再继承其他类。另外任务与代码没有分离，当多个线程执行一样的任务时需要多份任务代码</p>
<h3 id="方法二：使用Runnable配合Thread-推荐"><a href="#方法二：使用Runnable配合Thread-推荐" class="headerlink" title="方法二：使用Runnable配合Thread(推荐)"></a>方法二：使用Runnable配合Thread(推荐)</h3><figure class="highlight typescript"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs typescript"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Test2</span> &#123;<br>	<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-built_in">void</span> <span class="hljs-title function_">main</span>(<span class="hljs-params"><span class="hljs-built_in">String</span>[] args</span>) &#123;<br>		<span class="hljs-comment">//创建线程任务</span><br>		<span class="hljs-title class_">Runnable</span> r = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Runnable</span>() &#123;<br>			<span class="hljs-meta">@Override</span><br>			<span class="hljs-keyword">public</span> <span class="hljs-built_in">void</span> <span class="hljs-title function_">run</span>(<span class="hljs-params"></span>) &#123;<br>				<span class="hljs-title class_">System</span>.<span class="hljs-property">out</span>.<span class="hljs-title function_">println</span>(<span class="hljs-string">&quot;Runnable running&quot;</span>);<br>			&#125;<br>		&#125;;<br>		<span class="hljs-comment">//将Runnable对象传给Thread</span><br>		<span class="hljs-title class_">Thread</span> t = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Thread</span>(r);<br>		<span class="hljs-comment">//启动线程</span><br>		t.<span class="hljs-title function_">start</span>();<br>	&#125;<br>&#125;<br></code></pre></div></td></tr></table></figure>

<p>或者</p>
<figure class="highlight typescript"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs typescript"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">CreateThread2</span> &#123;<br>   <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">MyRunnable</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Runnable</span> &#123;<br><br>      <span class="hljs-meta">@Override</span><br>      <span class="hljs-keyword">public</span> <span class="hljs-built_in">void</span> <span class="hljs-title function_">run</span>(<span class="hljs-params"></span>) &#123;<br>         <span class="hljs-title class_">System</span>.<span class="hljs-property">out</span>.<span class="hljs-title function_">println</span>(<span class="hljs-string">&quot;my runnable running...&quot;</span>);<br>      &#125;<br>   &#125;<br><br>   <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-built_in">void</span> <span class="hljs-title function_">main</span>(<span class="hljs-params"><span class="hljs-built_in">String</span>[] args</span>) &#123;<br>      <span class="hljs-title class_">MyRunnable</span> myRunnable = <span class="hljs-keyword">new</span> <span class="hljs-title class_">MyRunnable</span>();<br>      <span class="hljs-title class_">Thread</span> thread = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Thread</span>(myRunnable);<br>      thread.<span class="hljs-title function_">start</span>();<br>   &#125;<br>&#125;<br></code></pre></div></td></tr></table></figure>

<p>通过实现Runnable接口，并且实现run()方法。在创建线程时作为参数传入该类的实例即可</p>
<h4 id="方法二的简化：使用lambda表达式简化操作"><a href="#方法二的简化：使用lambda表达式简化操作" class="headerlink" title="方法二的简化：使用lambda表达式简化操作"></a>方法二的简化：使用lambda表达式简化操作</h4><p><strong>当一个接口带有@FunctionalInterface注解时，是可以使用lambda来简化操作的</strong></p>
<p>所以方法二中的代码可以被简化为</p>
<figure class="highlight java"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Test2</span> &#123;<br>	<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> &#123;<br>		<span class="hljs-comment">//创建线程任务</span><br>		<span class="hljs-type">Runnable</span> <span class="hljs-variable">r</span> <span class="hljs-operator">=</span> () -&gt; &#123;<br>            <span class="hljs-comment">//直接写方法体即可</span><br>			System.out.println(<span class="hljs-string">&quot;Runnable running&quot;</span>);<br>			System.out.println(<span class="hljs-string">&quot;Hello Thread&quot;</span>);<br>		&#125;;<br>		<span class="hljs-comment">//将Runnable对象传给Thread</span><br>		<span class="hljs-type">Thread</span> <span class="hljs-variable">t</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Thread</span>(r);<br>		<span class="hljs-comment">//启动线程</span><br>		t.start();<br>	&#125;<br>&#125;<br></code></pre></div></td></tr></table></figure>

<h4 id="原理之-Thread-与-Runnable-的关系"><a href="#原理之-Thread-与-Runnable-的关系" class="headerlink" title="原理之 Thread 与 Runnable 的关系"></a>原理之 Thread 与 Runnable 的关系</h4><p>分析 Thread 的源码，理清它与 Runnable 的关系<br><strong>小结</strong></p>
<ul>
<li>方法1 是把线程和任务合并在了一起</li>
<li>方法2 是把线程和任务分开了</li>
<li>用 Runnable 更容易与线程池等高级 API 配合 用 Runnable 让任务类脱离了 Thread 继承体系，更灵活</li>
</ul>
<h3 id="方法三：使用FutureTask与Thread结合"><a href="#方法三：使用FutureTask与Thread结合" class="headerlink" title="方法三：使用FutureTask与Thread结合"></a>方法三：使用FutureTask与Thread结合</h3><p><strong>使用FutureTask可以用泛型指定线程的返回值类型（Runnable的run方法没有返回值）</strong></p>
<figure class="highlight java"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Test3</span> &#123;<br>	<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> ExecutionException, InterruptedException &#123;<br>        <span class="hljs-comment">//需要传入一个Callable对象</span><br>		FutureTask&lt;Integer&gt; task = <span class="hljs-keyword">new</span> <span class="hljs-title class_">FutureTask</span>&lt;Integer&gt;(<span class="hljs-keyword">new</span> <span class="hljs-title class_">Callable</span>&lt;Integer&gt;() &#123;<br>			<span class="hljs-meta">@Override</span><br>			<span class="hljs-keyword">public</span> Integer <span class="hljs-title function_">call</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception &#123;<br>				System.out.println(<span class="hljs-string">&quot;线程执行!&quot;</span>);<br>				Thread.sleep(<span class="hljs-number">1000</span>);<br>				<span class="hljs-keyword">return</span> <span class="hljs-number">100</span>;<br>			&#125;<br>		&#125;);<br><br>		<span class="hljs-type">Thread</span> <span class="hljs-variable">r1</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Thread</span>(task, <span class="hljs-string">&quot;t2&quot;</span>);<br>		r1.start();<br>		<span class="hljs-comment">//获取线程中方法执行后的返回结果</span><br>		System.out.println(task.get());<br>	&#125;<br>&#125;Copy<br></code></pre></div></td></tr></table></figure>

<p>或</p>
<figure class="highlight java"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">UseFutureTask</span> &#123;<br>   <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> ExecutionException, InterruptedException &#123;<br>      FutureTask&lt;String&gt; futureTask = <span class="hljs-keyword">new</span> <span class="hljs-title class_">FutureTask</span>&lt;&gt;(<span class="hljs-keyword">new</span> <span class="hljs-title class_">MyCall</span>());<br>      <span class="hljs-type">Thread</span> <span class="hljs-variable">thread</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Thread</span>(futureTask);<br>      thread.start();<br>      <span class="hljs-comment">// 获得线程运行后的返回值</span><br>      System.out.println(futureTask.get());<br>   &#125;<br>&#125;<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">MyCall</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Callable</span>&lt;String&gt; &#123;<br>   <span class="hljs-meta">@Override</span><br>   <span class="hljs-keyword">public</span> String <span class="hljs-title function_">call</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception &#123;<br>      <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;hello world&quot;</span>;<br>   &#125;<br>&#125;<br></code></pre></div></td></tr></table></figure>

<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>使用<strong>继承方式的好处是方便传参</strong>，你可以在子类里面添加成员变量，通过set方法设置参数或者通过构造函数进行传递，而如果使用Runnable方式，则只能使用主线程里面被声明为final的变量。<strong>不好的地方是Java不支持多继承</strong>，如果继承了Thread类，那么子类不能再继承其他类，而Runable则没有这个限制。<strong>前两种方式都没办法拿到任务的返回结果，但是Futuretask方式可以</strong></p>
<h2 id="2、原理之线程运行"><a href="#2、原理之线程运行" class="headerlink" title="2、原理之线程运行"></a>2、原理之线程运行</h2><h4 id="栈与栈帧"><a href="#栈与栈帧" class="headerlink" title="栈与栈帧"></a>栈与栈帧</h4><p>Java Virtual Machine Stacks （Java 虚拟机栈） 我们都知道 JVM 中由堆、栈、方法区所组成，其中栈内存是给谁用的呢？</p>
<ul>
<li>其实就是线程，每个线程启动后，虚拟机就会为其分配一块<strong>栈内存</strong></li>
<li>每个栈由多个栈帧（Frame）组成，对应着每次<strong>方法调用时所占用的内存</strong></li>
<li>每个线程只能有一个活动栈帧，对应着当前正在执行的那个方法</li>
</ul>
<h4 id="线程上下文切换（Thread-Context-Switch）"><a href="#线程上下文切换（Thread-Context-Switch）" class="headerlink" title="线程上下文切换（Thread Context Switch）"></a>线程上下文切换（Thread Context Switch）</h4><p>因为以下一些原因导致 cpu 不再执行当前的线程，转而执行另一个线程的代码</p>
<ul>
<li>线程的 cpu 时间片用完</li>
<li>垃圾回收 有更高优先级的线程需要运行</li>
<li>线程自己调用了 sleep、yield、wait、join、park、synchronized、lock 等方法</li>
</ul>
<p>当 Context Switch 发生时，需要由操作系统保存当前线程的状态，并恢复另一个线程的状态，Java 中对应的概念 就是程序计数器（Program Counter Register），它的作用是记住下一条 jvm 指令的执行地址，是线程私有的</p>
<ul>
<li>状态包括程序计数器、虚拟机栈中每个栈帧的信息，如局部变量、操作数栈、返回地址等</li>
<li>Context Switch 频繁发生会影响性能</li>
</ul>
<h2 id="3、常用方法"><a href="#3、常用方法" class="headerlink" title="3、常用方法"></a>3、常用方法</h2><h3 id="1-start-vs-run"><a href="#1-start-vs-run" class="headerlink" title="(1)start() vs run()"></a>(1)start() vs run()</h3><p>被创建的Thread对象直接调用重写的run方法时， run方法是在<strong>主线程</strong>中被执行的，而不是在我们所创建的线程中执行。如果想要在所创建的线程中执行run方法，需要使用Thread对象的start方法。start方法不可以不可以多次调用，会出现<code>IllegalThreadStateException</code>异常。</p>
<p>start()方法源码</p>
<figure class="highlight java"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">synchronized</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">start</span><span class="hljs-params">()</span> &#123;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * This method is not invoked for the main method thread or &quot;system&quot;</span><br><span class="hljs-comment">     * group threads created/set up by the VM. Any new functionality added</span><br><span class="hljs-comment">     * to this method in the future may have to also be added to the VM.</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * A zero status value corresponds to state &quot;NEW&quot;.</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-keyword">if</span> (threadStatus != <span class="hljs-number">0</span>)<br>        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">IllegalThreadStateException</span>();<br>    <span class="hljs-comment">/* Notify the group that this thread is about to be started</span><br><span class="hljs-comment">     * so that it can be added to the group&#x27;s list of threads</span><br><span class="hljs-comment">     * and the group&#x27;s unstarted count can be decremented. */</span><br>    group.add(<span class="hljs-built_in">this</span>);<br>    <span class="hljs-type">boolean</span> <span class="hljs-variable">started</span> <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;<br>    <span class="hljs-keyword">try</span> &#123;<br>        start0();<br>        started = <span class="hljs-literal">true</span>;<br>    &#125; <span class="hljs-keyword">finally</span> &#123;<br>        <span class="hljs-keyword">try</span> &#123;<br>            <span class="hljs-keyword">if</span> (!started) &#123;<br>                group.threadStartFailed(<span class="hljs-built_in">this</span>);<br>            &#125;<br>        &#125; <span class="hljs-keyword">catch</span> (Throwable ignore) &#123;<br>            <span class="hljs-comment">/* do nothing. If start0 threw a Throwable then</span><br><span class="hljs-comment">              it will be passed up the call stack */</span><br>        &#125;<br>    &#125;<br>&#125;<br><br><span class="hljs-keyword">private</span> <span class="hljs-keyword">native</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">start0</span><span class="hljs-params">()</span>;<br></code></pre></div></td></tr></table></figure>

<h3 id="2-sleep-与yield"><a href="#2-sleep-与yield" class="headerlink" title="(2)sleep()与yield()"></a>(2)sleep()与yield()</h3><h4 id="sleep-使线程阻塞"><a href="#sleep-使线程阻塞" class="headerlink" title="sleep (使线程阻塞)"></a><strong>sleep</strong> (使线程阻塞)</h4><ol>
<li><p>调用 sleep 会让当前线程从 <strong>RUNNING</strong> 进入 <strong>TIMED_WAITING</strong> 状态（阻塞），可通过state()方法查看</p>
</li>
<li><p>其它线程可以使用 <strong>interrupt</strong> 方法打断正在睡眠的线程，这时 sleep 方法会抛出 InterruptedException</p>
</li>
<li><p>睡眠结束后的线程未必会立刻得到执行</p>
</li>
<li><p>建议用 TimeUnit 的 sleep 代替 Thread 的 sleep 来获得更好的可读性 。如：</p>
<figure class="highlight java"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs java"><span class="hljs-comment">//休眠一秒</span><br>TimeUnit.SECONDS.sleep(<span class="hljs-number">1</span>);<br><span class="hljs-comment">//休眠一分钟</span><br>TimeUnit.MINUTES.sleep(<span class="hljs-number">1</span>);<br></code></pre></div></td></tr></table></figure>
<p>java.util.concurrent.TimeUnit#sleep源码</p>
<figure class="highlight java"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs java"><span class="hljs-comment">// 对Thread.sleep(ms, ns);进行了封装可读性更好</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">sleep</span><span class="hljs-params">(<span class="hljs-type">long</span> timeout)</span> <span class="hljs-keyword">throws</span> InterruptedException &#123;<br>    <span class="hljs-keyword">if</span> (timeout &gt; <span class="hljs-number">0</span>) &#123;<br>        <span class="hljs-type">long</span> <span class="hljs-variable">ms</span> <span class="hljs-operator">=</span> toMillis(timeout);<br>        <span class="hljs-type">int</span> <span class="hljs-variable">ns</span> <span class="hljs-operator">=</span> excessNanos(timeout, ms);<br>        Thread.sleep(ms, ns);<br>    &#125;<br>&#125;<br></code></pre></div></td></tr></table></figure></li>
</ol>
<p>哪个进程执行sleep()方法就会使哪个进程休眠</p>
<p><strong>sleep</strong> <strong>实现</strong></p>
<p>在没有利用 cpu 来计算时，不要让 while(true) 空转浪费 cpu，这时可以使用 yield 或 sleep 来让出 cpu 的使用权给其他程序</p>
<figure class="highlight java"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs java"><span class="hljs-keyword">while</span>(<span class="hljs-literal">true</span>) &#123;<br> 	<span class="hljs-keyword">try</span> &#123;<br> 		Thread.sleep(<span class="hljs-number">50</span>);<br> 	&#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;<br> 		e.printStackTrace();<br> 	&#125;<br>&#125;<br></code></pre></div></td></tr></table></figure>

<p>可以用 wait 或 条件变量达到类似的效果</p>
<p>不同的是，后两种都需要加锁，并且需要相应的唤醒操作，一般适用于要进行同步的场景</p>
<p>sleep 适用于无需锁同步的场景</p>
<h4 id="yield-（让出当前线程）"><a href="#yield-（让出当前线程）" class="headerlink" title="yield （让出当前线程）"></a>yield （让出当前线程）</h4><ol>
<li>调用 yield 会让当前线程从 <strong>RUNNING进入 RUNNABLE</strong>，然后调度执行其它线程。（仍然有可能被执行）任务调度器会将时间片分配给就绪状态的线程，不会分配给阻塞状态的线程，这也是yield和sleep最大的区别</li>
<li>具体的实现依赖于操作系统的任务调度器</li>
</ol>
<h4 id="线程优先级"><a href="#线程优先级" class="headerlink" title="线程优先级"></a>线程优先级</h4><figure class="highlight java"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * The minimum priority that a thread can have.</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">static</span> <span class="hljs-type">int</span> <span class="hljs-variable">MIN_PRIORITY</span> <span class="hljs-operator">=</span> <span class="hljs-number">1</span>;<br><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * The default priority that is assigned to a thread.</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">static</span> <span class="hljs-type">int</span> <span class="hljs-variable">NORM_PRIORITY</span> <span class="hljs-operator">=</span> <span class="hljs-number">5</span>;<br><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * The maximum priority that a thread can have.</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">static</span> <span class="hljs-type">int</span> <span class="hljs-variable">MAX_PRIORITY</span> <span class="hljs-operator">=</span> <span class="hljs-number">10</span>;<br></code></pre></div></td></tr></table></figure>

<ul>
<li><p>线程优先级会提示（hint）调度器优先调度该线程，但它仅仅是一个提示，调度器可以忽略它</p>
</li>
<li><p>如果 cpu 比较忙，那么优先级高的线程会获得更多的时间片，但 cpu 闲时，优先级几乎没作用</p>
</li>
<li><p>设置方法：</p>
<figure class="highlight awk"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs awk">thread1.setPriority(Thread.MAX_PRIORITY); <span class="hljs-regexp">//</span>设置为优先级最高<br></code></pre></div></td></tr></table></figure></li>
</ul>
<h3 id="3-join-方法"><a href="#3-join-方法" class="headerlink" title="(3)join()方法"></a>(3)join()方法</h3><p>用于等待某个线程结束。哪个线程内调用join()方法，就等待哪个线程结束，然后再去执行其他线程。</p>
<p>如在主线程中调用ti.join()，则是主线程等待t1线程结束</p>
<figure class="highlight java"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs java"><span class="hljs-type">Thread</span> <span class="hljs-variable">thread</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Thread</span>();<br><span class="hljs-comment">//等待直到thread线程执行结束</span><br>thread.join();<br><span class="hljs-comment">//最多等待1000ms,如果线程提前结束，则会直接执行接下来的语句，不会等够1000ms</span><br>thread.join(<span class="hljs-number">1000</span>);<br></code></pre></div></td></tr></table></figure>

<h3 id="4-interrupt-方法"><a href="#4-interrupt-方法" class="headerlink" title="(4)interrupt()方法"></a>(4)interrupt()方法</h3><p>用于打断<strong>阻塞</strong>(sleep wait join…)的线程。 处于阻塞状态的线程，CPU不会给其分配时间片。</p>
<ul>
<li><p>如果一个线程在在运行中被打断，打断标记会被置为true。</p>
</li>
<li><p>如果是打断因sleep wait join方法而被阻塞的线程，会将打断标记置为false</p>
<figure class="highlight java"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs java"><span class="hljs-comment">// 源码注释说明</span><br><span class="hljs-comment">/*If this thread is blocked in an invocation of the wait(), wait(long), or wait(long, int) methods of the Object class, or of the join(), join(long), join(long, int), sleep(long), or sleep(long, int), methods of this class, then its interrupt status will be cleared and it will receive an InterruptedException.*/</span><br><br><span class="hljs-comment">//用于查看打断标记，返回值被boolean类型</span><br>t1.isInterrupted();<br></code></pre></div></td></tr></table></figure></li>
</ul>
<p>正常运行的线程在被打断后，<strong>不会停止</strong>，会继续执行。如果要让线程在被打断后停下来，需要<strong>使用打断标记来判断</strong>。</p>
<figure class="highlight java"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs java"><span class="hljs-keyword">while</span>(<span class="hljs-literal">true</span>) &#123;<br>    <span class="hljs-keyword">if</span>(Thread.currentThread().isInterrupted()) &#123;  <span class="hljs-comment">// 条件部分即为打断标记</span><br>        <span class="hljs-keyword">break</span>;<br>    &#125;<br>&#125;<br></code></pre></div></td></tr></table></figure>

<p><strong>打断</strong> <strong>park</strong> <strong>线程</strong></p>
<p>打断 park 线程, 不会清空打断状态</p>
<figure class="highlight java"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs java"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">test3</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> InterruptedException &#123;<br>    <span class="hljs-type">Thread</span> <span class="hljs-variable">t1</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Thread</span>(() -&gt; &#123;<br>        log.debug(<span class="hljs-string">&quot;park...&quot;</span>);<br>        LockSupport.park();  <br>        log.debug(<span class="hljs-string">&quot;unpark...&quot;</span>);<br>        log.debug(<span class="hljs-string">&quot;打断状态：&#123;&#125;&quot;</span>, Thread.currentThread().isInterrupted());<br>    &#125;, <span class="hljs-string">&quot;t1&quot;</span>);<br>    t1.start();<br>    sleep(<span class="hljs-number">1</span>);<br>    t1.interrupt();<br>&#125;<br></code></pre></div></td></tr></table></figure>

<p>输出</p>
<figure class="highlight stylus"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs stylus"><span class="hljs-number">02</span>:<span class="hljs-number">01</span>:<span class="hljs-number">58.678</span> c<span class="hljs-selector-class">.Test14</span> <span class="hljs-selector-attr">[t1]</span> - park...<br><span class="hljs-number">02</span>:<span class="hljs-number">01</span>:<span class="hljs-number">59.681</span> c<span class="hljs-selector-class">.Test14</span> <span class="hljs-selector-attr">[t1]</span> - unpark...<br><span class="hljs-number">02</span>:<span class="hljs-number">01</span>:<span class="hljs-number">59.681</span> c<span class="hljs-selector-class">.Test14</span> <span class="hljs-selector-attr">[t1]</span> - 打断状态：true<br></code></pre></div></td></tr></table></figure>

<p>打断之后如果想再次park 的话，由于打断标记已经是 true, 则 park 会失效。在条件判定处可以使用interrupt()在返回打断状态的同时也会清除打断标记。</p>
<h4 id="interrupt方法的应用——两阶段终止模式"><a href="#interrupt方法的应用——两阶段终止模式" class="headerlink" title="interrupt方法的应用——两阶段终止模式"></a><strong>interrupt方法的应用</strong>——两阶段终止模式</h4><p><strong>终止模式之两阶段终止模式</strong></p>
<p>Two Phase Termination</p>
<p>在一个线程 T1 中如何“优雅”终止线程 T2？这里的【优雅】指的是给 T2 一个料理后事的机会。</p>
<p><strong>1.</strong> <strong>错误思路</strong></p>
<p>使用线程对象的 stop() 方法停止线程</p>
<ul>
<li>stop 方法会真正杀死线程，如果这时线程锁住了共享资源，那么当它被杀死后就再也没有机会释放锁，其它线程将永远无法获取锁</li>
</ul>
<p>使用 System.exit(int) 方法停止线程</p>
<ul>
<li>目的仅是停止一个线程，但这种做法会让整个程序都停止</li>
</ul>
<p><strong>2.</strong> <strong>两阶段终止模式</strong></p>
<p>当我们在执行线程一时，想要终止线程二，这是就需要使用interrupt方法来<strong>优雅</strong>的停止线程二。</p>
<p><img src="https://blog-chy.oss-cn-shenzhen.aliyuncs.com/static/image-20220306013433459.png" srcset="/img/loading.gif" lazyload></p>
<p>示例代码：</p>
<figure class="highlight java"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs java"><span class="hljs-meta">@Slf4j(topic = &quot;c.TwoPhaseTermination&quot;)</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Test13</span> &#123;<br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> InterruptedException &#123;<br>        <span class="hljs-type">TwoPhaseTermination</span> <span class="hljs-variable">tpt</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">TwoPhaseTermination</span>();<br>        tpt.start();<br><br>        Thread.sleep(<span class="hljs-number">3500</span>);<br>        log.debug(<span class="hljs-string">&quot;停止监控&quot;</span>);<br>        tpt.stop();<br>    &#125;<br>&#125;<br><br><span class="hljs-meta">@Slf4j(topic = &quot;c.TwoPhaseTermination&quot;)</span><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">TwoPhaseTermination</span> &#123;<br>    <span class="hljs-comment">// 监控线程</span><br>    <span class="hljs-keyword">private</span> Thread monitorThread;<br><br>    <span class="hljs-comment">// 启动监控线程</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">start</span><span class="hljs-params">()</span> &#123;<br>        monitorThread = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Thread</span>(() -&gt; &#123;<br>            <span class="hljs-keyword">while</span> (<span class="hljs-literal">true</span>) &#123;<br>                <span class="hljs-type">Thread</span> <span class="hljs-variable">current</span> <span class="hljs-operator">=</span> Thread.currentThread();<br>                <span class="hljs-comment">// 是否被打断</span><br>                <span class="hljs-keyword">if</span> (current.isInterrupted()) &#123;<br>                    log.debug(<span class="hljs-string">&quot;料理后事&quot;</span>);<br>                    <span class="hljs-keyword">break</span>;<br>                &#125;<br>                <span class="hljs-keyword">try</span> &#123;<br>                    Thread.sleep(<span class="hljs-number">1000</span>);<br>                    log.debug(<span class="hljs-string">&quot;执行监控记录&quot;</span>);<br>                &#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;<br>                    e.printStackTrace();<br>                    <span class="hljs-comment">// sleep期间被打断出现异常会清除打断标记 这里是重置打断标记</span><br>                    Thread.interrupted();   <br>                &#125;<br>            &#125;<br>        &#125;, <span class="hljs-string">&quot;monitor&quot;</span>);<br>        monitorThread.start();<br>    &#125;<br><br>    <span class="hljs-comment">// 停止监控线程</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">stop</span><span class="hljs-params">()</span> &#123;<br>        monitorThread.interrupt();<br>    &#125;<br>&#125;<br></code></pre></div></td></tr></table></figure>

<h3 id="5-不推荐使用的打断方法"><a href="#5-不推荐使用的打断方法" class="headerlink" title="(5)不推荐使用的打断方法"></a>(5)不推荐使用的打断方法</h3><ul>
<li>stop方法 停止线程运行（可能造成共享资源无法被释放，其他线程无法使用这些共享资源）</li>
<li>suspend（暂停线程）&#x2F;resume（恢复线程）方法</li>
</ul>
<h3 id="6-守护线程"><a href="#6-守护线程" class="headerlink" title="(6)守护线程"></a>(6)守护线程</h3><p>当JAVA进程中有多个线程在执行时，只有当所有非守护线程都执行完毕后，JAVA进程才会结束。<strong>但当非守护线程全部执行完毕后，守护线程无论是否执行完毕，也会一同结束。</strong></p>
<figure class="highlight crmsh"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs crmsh">//将线程设置为守护线程, 默认为<span class="hljs-literal">false</span><br><span class="hljs-literal">monitor</span>.setDaemon(<span class="hljs-literal">true</span>);Copy<br></code></pre></div></td></tr></table></figure>

<p><strong>守护线程的应用</strong></p>
<ul>
<li>垃圾回收器线程就是一种守护线程</li>
<li>Tomcat 中的 Acceptor 和 Poller 线程都是守护线程，所以 Tomcat 接收到 shutdown 命令后，不会等 待它们处理完当前请求</li>
</ul>
<h2 id="4、线程的状态"><a href="#4、线程的状态" class="headerlink" title="4、线程的状态"></a>4、线程的状态</h2><h3 id="1-五种状态"><a href="#1-五种状态" class="headerlink" title="(1)五种状态"></a>(1)五种状态</h3><p>这是从 <strong>操作系统</strong> 层面来描述的</p>
<p><img src="https://blog-chy.oss-cn-shenzhen.aliyuncs.com/static/image-20220306170958072.png" srcset="/img/loading.gif" lazyload></p>
<ul>
<li>【初始状态】仅是在语言层面创建了线程对象，还未与操作系统线程关联（例如线程调用了start方法）</li>
<li>【可运行状态】（就绪状态）指该线程已经被创建（与操作系统线程关联），可以由 CPU 调度执行</li>
<li>【运行状态】指获取了 CPU 时间片运行中的状态<ul>
<li>当 CPU 时间片用完，会从【运行状态】转换至【可运行状态】，会导致线程的上下文切换</li>
</ul>
</li>
<li>【阻塞状态】<ul>
<li>如果调用了阻塞 API，如 BIO 读写文件，这时该线程实际不会用到 CPU，会导致线程上下文切换，进入 【阻塞状态】</li>
<li>等 BIO 操作完毕，会由操作系统唤醒阻塞的线程，转换至【可运行状态】</li>
<li>与【可运行状态】的区别是，对【阻塞状态】的线程来说只要它们一直不唤醒，调度器就一直不会考虑调度它们</li>
</ul>
</li>
<li>【终止状态】表示线程已经执行完毕，生命周期已经结束，不会再转换为其它状态</li>
</ul>
<h3 id="2-六种状态"><a href="#2-六种状态" class="headerlink" title="(2)六种状态"></a>(2)六种状态</h3><p>这是从 <strong>Java API</strong> 层面来描述的<br>根据 Thread.State 枚举，分为六种状态</p>
<p><img src="https://blog-chy.oss-cn-shenzhen.aliyuncs.com/static/image-20220306171032978.png" srcset="/img/loading.gif" lazyload></p>
<ul>
<li><strong>NEW</strong> 线程刚被创建，但是还没有调用 start() 方法</li>
<li><strong>RUNNABLE</strong> 当调用了 start() 方法之后，注意，Java API 层面的 RUNNABLE 状态涵盖了操作系统层面的 【可运行状态】、【运行状态】和【阻塞状态】（由于 BIO 导致的线程阻塞，在 Java 里无法区分，仍然认为 是可运行）</li>
<li><strong>BLOCKED ， WAITING ， TIMED_WAITING</strong> 都是 <strong>Java API 层面</strong>对【阻塞状态】的细分，如sleep就位TIMED_WAITING， join为WAITING状态。后面会在状态转换一节详述。</li>
<li><strong>TERMINATED</strong> 当线程代码运行结束</li>
</ul>
<h2 id="应用案例：统筹"><a href="#应用案例：统筹" class="headerlink" title="应用案例：统筹"></a>应用案例：统筹</h2><h3 id="解法一：join"><a href="#解法一：join" class="headerlink" title="解法一：join"></a>解法一：join</h3><p><img src="https://blog-chy.oss-cn-shenzhen.aliyuncs.com/static/image-20220306172047939.png" srcset="/img/loading.gif" lazyload></p>
<figure class="highlight java"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> &#123;<br>    <span class="hljs-type">Thread</span> <span class="hljs-variable">t1</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Thread</span>(()-&gt;&#123;<br>        log.debug(<span class="hljs-string">&quot;洗水壶&quot;</span>);<br>        sleep(<span class="hljs-number">1</span>);<br>        log.debug(<span class="hljs-string">&quot;烧开水&quot;</span>);<br>        sleep(<span class="hljs-number">10</span>);<br>    &#125;,<span class="hljs-string">&quot;wang&quot;</span>);<br>    <span class="hljs-type">Thread</span> <span class="hljs-variable">t2</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Thread</span>(()-&gt;&#123;<br>        log.debug(<span class="hljs-string">&quot;洗茶壶&quot;</span>);<br>        sleep(<span class="hljs-number">1</span>);<br>        log.debug(<span class="hljs-string">&quot;洗茶杯&quot;</span>);<br>        sleep(<span class="hljs-number">2</span>);<br>        log.debug(<span class="hljs-string">&quot;拿茶叶&quot;</span>);<br>        sleep(<span class="hljs-number">1</span>);<br>        <span class="hljs-keyword">try</span> &#123;<br>            t1.join();<br>        &#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;<br>            e.printStackTrace();<br>        &#125;<br>        log.debug(<span class="hljs-string">&quot;泡茶&quot;</span>);<br>    &#125;,<span class="hljs-string">&quot;zhang&quot;</span>);<br>    t1.start();<br>    t2.start();<br>&#125;<br></code></pre></div></td></tr></table></figure>

<p>结果：</p>
<figure class="highlight stylus"><table><tr><td class="gutter hljs"><div class="hljs code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></div></td><td class="code"><div class="hljs code-wrapper"><pre><code class="hljs stylus"><span class="hljs-number">17</span>:<span class="hljs-number">01</span>:<span class="hljs-number">29.247</span> c<span class="hljs-selector-class">.Test16</span> <span class="hljs-selector-attr">[wang]</span> - 洗水壶<br><span class="hljs-number">17</span>:<span class="hljs-number">01</span>:<span class="hljs-number">29.247</span> c<span class="hljs-selector-class">.Test16</span> <span class="hljs-selector-attr">[zhang]</span> - 洗茶壶<br><span class="hljs-number">17</span>:<span class="hljs-number">01</span>:<span class="hljs-number">30.264</span> c<span class="hljs-selector-class">.Test16</span> <span class="hljs-selector-attr">[zhang]</span> - 洗茶杯<br><span class="hljs-number">17</span>:<span class="hljs-number">01</span>:<span class="hljs-number">30.264</span> c<span class="hljs-selector-class">.Test16</span> <span class="hljs-selector-attr">[wang]</span> - 烧开水<br><span class="hljs-number">17</span>:<span class="hljs-number">01</span>:<span class="hljs-number">32.266</span> c<span class="hljs-selector-class">.Test16</span> <span class="hljs-selector-attr">[zhang]</span> - 拿茶叶<br><span class="hljs-number">17</span>:<span class="hljs-number">01</span>:<span class="hljs-number">40.273</span> c<span class="hljs-selector-class">.Test16</span> <span class="hljs-selector-attr">[zhang]</span> - 泡茶<br></code></pre></div></td></tr></table></figure>

<ul>
<li><p>上面模拟的是小王等老王的水烧开了，小王泡茶，如果反过来要实现老王等小王的茶叶拿来了，老王泡茶呢？代码最好能适应两种情况。</p>
</li>
<li><p>上面的两个线程其实是各执行各的，如果要模拟老王把水壶交给小王泡茶，或模拟小王把茶叶交给老王泡茶呢</p>
</li>
</ul>

            </div>
            <hr>
            <div>
              <div class="post-metas mb-3">
                
                  <div class="post-meta mr-3">
                    <i class="iconfont icon-category"></i>
                    
                      <a class="hover-with-bg" href="/categories/Java/">Java</a>
                    
                  </div>
                
                
                  <div class="post-meta">
                    <i class="iconfont icon-tags"></i>
                    
                      <a class="hover-with-bg" href="/tags/JUC/">JUC</a>
                    
                  </div>
                
              </div>
              
                <p class="note note-warning">
                  
                    本博客所有文章除特别声明外，均采用 <a target="_blank" href="https://creativecommons.org/licenses/by-sa/4.0/deed.zh" rel="nofollow noopener noopener">CC BY-SA 4.0 协议</a> ，转载请注明出处！
                  
                </p>
              
              
                <div class="post-prevnext">
                  <article class="post-prev col-6">
                    
                    
                      <a href="/2022/03/07/Web%E5%88%86%E9%A1%B5%E6%9F%A5%E8%AF%A2/">
                        <i class="iconfont icon-arrowleft"></i>
                        <span class="hidden-mobile">Web分页查询</span>
                        <span class="visible-mobile">Previous</span>
                      </a>
                    
                  </article>
                  <article class="post-next col-6">
                    
                    
                      <a href="/2022/03/03/StringBuffer%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E7%9A%84%E5%AE%9E%E7%8E%B0/">
                        <span class="hidden-mobile">StringBuffer线程安全的实现</span>
                        <span class="visible-mobile">Next</span>
                        <i class="iconfont icon-arrowright"></i>
                      </a>
                    
                  </article>
                </div>
              
            </div>

            
              <!-- Comments -->
              <article class="comments" id="comments" lazyload>
                
                  
                
                

              </article>
            
          </article>
        </div>
      </div>
    </div>
    
      <div class="d-none d-lg-block col-lg-2 toc-container" id="toc-ctn">
        <div id="toc">
  <p class="toc-header"><i class="iconfont icon-list"></i>&nbsp;TOC</p>
  <div class="toc-body" id="toc-body"></div>
</div>

      </div>
    
  </div>
</div>

<!-- Custom -->


    

    
      <a id="scroll-top-button" aria-label="TOP" href="#" role="button">
        <i class="iconfont icon-arrowup" aria-hidden="true"></i>
      </a>
    

    
      <div class="modal fade" id="modalSearch" tabindex="-1" role="dialog" aria-labelledby="ModalLabel"
     aria-hidden="true">
  <div class="modal-dialog modal-dialog-scrollable modal-lg" role="document">
    <div class="modal-content">
      <div class="modal-header text-center">
        <h4 class="modal-title w-100 font-weight-bold">Search</h4>
        <button type="button" id="local-search-close" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body mx-3">
        <div class="md-form mb-5">
          <input type="text" id="local-search-input" class="form-control validate">
          <label data-error="x" data-success="v"
                 for="local-search-input">keyword</label>
        </div>
        <div class="list-group" id="local-search-result"></div>
      </div>
    </div>
  </div>
</div>
    

    
  </main>

  <footer class="text-center mt-5 py-3">
  <div class="footer-content">
     <a href="https://hexo.io" target="_blank" rel="nofollow noopener"><span>Hexo</span></a> <i class="iconfont icon-love"></i> <a href="https://github.com/fluid-dev/hexo-theme-fluid" target="_blank" rel="nofollow noopener"><span>Fluid</span></a> 
  </div>
  
  <div class="statistics">
    
    

    
      
        <!-- 不蒜子统计PV -->
        <span id="busuanzi_container_site_pv" style="display: none">
            总访问量 
            <span id="busuanzi_value_site_pv"></span>
             次
          </span>
      
      
        <!-- 不蒜子统计UV -->
        <span id="busuanzi_container_site_uv" style="display: none">
            总访客数 
            <span id="busuanzi_value_site_uv"></span>
             人
          </span>
      
    
  </div>


  

  
</footer>


  <!-- SCRIPTS -->
  
  <script  src="https://cdn.jsdelivr.net/npm/nprogress@0/nprogress.min.js" ></script>
  <link  rel="stylesheet" href="https://cdn.jsdelivr.net/npm/nprogress@0/nprogress.min.css" />

  <script>
    NProgress.configure({"showSpinner":false,"trickleSpeed":100})
    NProgress.start()
    window.addEventListener('load', function() {
      NProgress.done();
    })
  </script>


<script  src="https://cdn.jsdelivr.net/npm/jquery@3/dist/jquery.min.js" ></script>
<script  src="https://cdn.jsdelivr.net/npm/bootstrap@4/dist/js/bootstrap.min.js" ></script>
<script  src="/js/events.js" ></script>
<script  src="/js/plugins.js" ></script>

<!-- Plugins -->


  <script  src="/js/local-search.js" ></script>



  
    <script  src="/js/img-lazyload.js" ></script>
  



  



  
    <script  src="https://cdn.jsdelivr.net/npm/tocbot@4/dist/tocbot.min.js" ></script>
  
  
    <script  src="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3/dist/jquery.fancybox.min.js" ></script>
  
  
    <script  src="https://cdn.jsdelivr.net/npm/anchor-js@4/anchor.min.js" ></script>
  
  
    <script defer src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js" ></script>
  



  <script defer src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js" ></script>




  <script  src="https://cdn.jsdelivr.net/npm/typed.js@2/lib/typed.min.js" ></script>
  <script>
    (function (window, document) {
      var typing = Fluid.plugins.typing;
      var title = document.getElementById('subtitle').title;
      
        typing(title);
      
    })(window, document);
  </script>















<!-- 主题的启动项 保持在最底部 -->
<script  src="/js/boot.js" ></script>


</body>
</html>
